package com.antoco.operation.bigscreen.service.autogeneration.impl;

import com.alibaba.fastjson.JSONArray;
import com.antoco.operation.bigscreen.model.entity.Sensor;
import com.antoco.operation.bigscreen.model.entity.SensorReport;
import com.antoco.operation.bigscreen.model.entity.SensorWarn;
import com.antoco.operation.bigscreen.repository.SensorReportRepository;
import com.antoco.operation.bigscreen.repository.SensorRepository;
import com.antoco.operation.bigscreen.repository.SensorWarnRepository;
import com.antoco.operation.bigscreen.service.WebSocketServer;
import com.antoco.operation.bigscreen.service.autogeneration.intf.SensorReportService;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigInteger;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
public class SensorReportServiceImpl extends AbstractModelServiceImpl implements SensorReportService {

    @Autowired
    private SensorReportRepository repository;

    @Autowired
    private SensorRepository sensorRepository;

    @Autowired
    private SensorWarnRepository sensorWarnRepository;

    @Autowired
    private WebSocketServer webSocketServer;

    @Override
    public SensorReport save(SensorReport report) {
        Optional<Sensor> currentSensor = sensorRepository.findById(report.getSensorId());
        SensorReport saved = null;
        if (currentSensor.isPresent()) {
            try {
                Sensor original = currentSensor.get();
                fillStatsticUpdates(report, original);
                saved = repository.save(report);
                original.setNewestReportId(saved.getId());
                Sensor sensorSaved = sensorRepository.save(original);
                List<SensorWarn> sensorWarns = sensorWarnRepository.findBySensorId(String.valueOf(report.getSensorId()));
                // 推送给传感器频道
                List<Sensor> list = new ArrayList<Sensor>();
                list.add(sensorSaved);
                String channelName = "SENSO";
                JSONObject json = new JSONObject();
                json.put("sensor", list);
                json.put("sensor_warn", sensorWarns);
                JSONObject result = new JSONObject();
                result.put("data", json);
                result.put("channel", channelName.concat("@").concat(String.valueOf(report.getSensorId())));
                webSocketServer.send2Channel(channelName, result.toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return saved;
    }

    @Override
    public List<SensorReport> save(List<SensorReport> entities) {
        entities.forEach(report -> {
            Optional<Sensor> currentSensor = sensorRepository.findById(report.getSensorId());
            if (currentSensor.isPresent()) {
                Sensor original = currentSensor.get();
                fillStatsticUpdates(report, original);
                sensorRepository.save(original);
            }
        });
        return (List<SensorReport>) repository.saveAll(entities);
    }

    @Override
    public void deleteById(Long id) {
        repository.deleteById(id);
    }

    @Override
    public Optional<SensorReport> findById(Long id) {
        return repository.findById(id);
    }

    @Override
    public List<SensorReport> findAll() {
        return (List<SensorReport>) repository.findAll();
    }

    @Override
    public Page<SensorReport> findAll(Pageable pageable) {
        Page<SensorReport> entityPage = repository.findAll(pageable);
        List<SensorReport> entities = entityPage.getContent();
        return new PageImpl<>(entities, pageable, entityPage.getTotalElements());
    }

    @Override
    public SensorReport update(SensorReport entity, Long id) {
        Optional<SensorReport> optional = findById(id);
        if (optional.isPresent()) {
            return save(entity);
        }
        return null;
    }

    /**
     * 根据传感器标识获取通信记录昨天和今天半个小时段的数据
     *
     * @param id
     * @return
     */
    public JSONObject findAllBy(Long id) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        String today = formatter.format(new Date()) + " ";

        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, -1);
        Date d = cal.getTime();
        SimpleDateFormat sp = new SimpleDateFormat("yyyy-MM-dd");
        String yesterday = sp.format(d) + " ";//获取昨天日期

        List<String> yesterdayList = new ArrayList<String>();
        List<String> todayList = new ArrayList<String>();

        // String sql ="select * from sensor_report where sensor_id = :sensorId and gen_time >= :startTime and gen_time <= :endTime order by gen_time DESC";
        String[] strings = new String[]{"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"};
        //查询昨天半个小时段的48条数据
        for (int i = 0; i < strings.length; i++) {
            // List<SensorReport> list1 = jdbcNativeQuery.queryForList(sql,map1,SensorReport.class);
            List<SensorReport> list1 = repository.findForTodayAndYestoday(id, yesterday + strings[i] + ":00:00", yesterday + strings[i] + ":29:59");
            if (list1.size() > 0) {
                yesterdayList.add(list1.get(0).getReportVal());
            } else {
                yesterdayList.add("||");
            }
            // List<SensorReport> list2 = jdbcNativeQuery.queryForList(sql,map2,SensorReport.class);
            List<SensorReport> list2 = repository.findForTodayAndYestoday(id, yesterday + strings[i] + ":30:00", yesterday + strings[i] + ":59:59");
            if (list2.size() > 0) {
                yesterdayList.add(list2.get(0).getReportVal());
            } else {
                yesterdayList.add("||");
            }
        }
        String[] todayHours = getTodayHours();
        //查询截止当前时间半个小时段的数据
        for (int i = 0; i < todayHours.length; i++) {
            List<SensorReport> list3 = repository.findForTodayAndYestoday(id, today + todayHours[i] + ":00:00", today + todayHours[i] + ":29:59");
            if (list3.size() > 0) {
                todayList.add(list3.get(0).getReportVal());
            } else {
                todayList.add("||");
            }
            // List<SensorReport> list4 = jdbcNativeQuery.queryForList(sql,map4,SensorReport.class);
            //比较当前时间和半小时段时间的大小
            String maxtodayTime = today + todayHours[i] + ":30:00";
            SimpleDateFormat formatt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String nowTime = formatt.format(new Date());
            int compare = compare_date(nowTime, maxtodayTime);
            if (compare == 1) {
                List<SensorReport> list4 = repository.findForTodayAndYestoday(id, today + todayHours[i] + ":30:00", today + todayHours[i] + ":59:59");
                if (list4.size() > 0) {
                    todayList.add(list4.get(0).getReportVal());
                } else {
                    todayList.add("||");
                }
            }
        }

        List<String> yesterdayNewList = new ArrayList<String>();
        List<String> todayNewList = new ArrayList<String>();
        //昨天48个半小时的数据逻辑处理
        for (int i = 0; i < yesterdayList.size(); i++) {
            if (i == 0) {
                if (yesterdayList.get(0).equals("||")) {
                    yesterdayNewList.add("0");
                } else {
                    yesterdayNewList.add(yesterdayList.get(0));
                }
            } else {
                if (yesterdayList.get(i).equals("||")) {
                    yesterdayNewList.add(yesterdayNewList.get(i - 1));
                } else {
                    yesterdayNewList.add(yesterdayList.get(i));
                }
            }
        }
        //今天截止当前时间半小时的数据逻辑处理
        for (int i = 0; i < todayList.size(); i++) {
            if (i == 0) {
                if (todayList.get(0).equals("||")) {
                    todayNewList.add("0");
                } else {
                    todayNewList.add(todayList.get(0));
                }
            } else {
                if (todayList.get(i).equals("||")) {
                    todayNewList.add(todayNewList.get(i - 1));
                } else {
                    todayNewList.add(todayList.get(i));
                }
            }
        }

        JSONArray yesterdayArray = new JSONArray();
        yesterdayArray.addAll(yesterdayNewList);
        JSONArray todayArray = new JSONArray();
        todayArray.addAll(todayNewList);

        JSONObject json = new JSONObject();
        json.put("yesterday", yesterdayArray);
        json.put("today", todayArray);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("sensor_report", json);
        JSONObject jsonObjectEnd = new JSONObject();
        jsonObjectEnd.put("channel", "SENSOR_REPORT@".concat(String.valueOf(id)));
        jsonObjectEnd.put("data", jsonObject);
        return jsonObjectEnd;
    }

    private void fillStatsticUpdates(SensorReport latest, Sensor original) {
        if (latest.getReportVal() == null) return;
        // set recstate, gentime, newval
        original.setNewestVal(latest.getReportVal());
        original.setRecState(latest.getRecState());
        DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        original.setNewestReportTime(format.format(LocalDateTime.now()));
        original.setReportNum(original.getReportNum() == null ? 1 : original.getReportNum() + 1);
        // the last recorded date, if current date is bigger than last recorded date, new day begins
        LocalDate lastGeneratedLocalDate = original.getGenTime() == null ? LocalDate.now() : original.getGenTime().toLocalDateTime().toLocalDate();
        original.setReportNumToday(LocalDate.now().isAfter(lastGeneratedLocalDate) ? 1 : original.getReportNumToday() + 1);

        // 用table中的原始阈值，来计算alarm,warn
        fillAlarmAndWarn(latest, original);

        Double newVal = Double.valueOf(latest.getReportVal());
        // 计算历史数据
        fillHistorical(newVal, original);
        // 计算当日和上一日对比数据
        fillYesterdayAndToday(newVal, original);
        original.setGenTime(latest.getGenTime());
    }

    private void fillYesterdayAndToday(Double latest, Sensor original) {
        // 当天最大最小平均
        int todayReportNum = original.getReportNumToday();
        LocalDate previousDate = original.getGenTime() == null ? LocalDate.now() : original.getGenTime().toLocalDateTime().toLocalDate();
        if (LocalDate.now().isAfter(previousDate)) {
            // update yesterday's value
            original.setPrevMinVal(original.getThisMinVal());
            original.setPrevMaxVal(original.getThisMaxVal());
            original.setPrevAveVal(original.getThisAveVal());

            // 上一日最小值，最大值，平均值变化趋势， 变化率
            original.setPrevMinChgQusi(original.getThisMinChgQusi());
            original.setPrevMaxChgQusi(original.getThisMaxChgQusi());
            original.setPrevAveChgQusi(original.getPrevAveChgQusi());

            //reset today's first record
            original.setThisMinVal(String.format("%.2f", latest));
            original.setThisMaxVal(String.format("%.2f", latest));
            original.setThisAveVal(String.format("%.2f", latest));

        } else {
            Double minValToday = Double.valueOf(original.getThisMinVal());
            Double maxValToday = Double.valueOf(original.getThisMaxVal());
            Double avgValToday = Double.valueOf(original.getThisAveVal());
            String[] result = calcNewMinMaxAvg(latest, minValToday, maxValToday, avgValToday, BigInteger.valueOf(todayReportNum));
            original.setThisMinVal(result[0]);
            original.setThisMaxVal(result[1]);
            original.setThisAveVal(result[2]);
        }

        // 当天最小值，最大值，平均值变化趋势， 变化率
        if (original.getReportNumToday() > 0 && original.getReportNum() - original.getReportNumToday() > 0) {
            String[] result = calculateChangeRateAndChangeTrends(
                    Double.valueOf(original.getThisMinVal()), Double.valueOf(original.getThisMaxVal()), Double.valueOf(original.getThisAveVal()),
                    Double.valueOf(original.getPrevMinVal()), Double.valueOf(original.getPrevMaxVal()), Double.valueOf(original.getPrevAveVal()));
            original.setThisMinChgLv(result[0]);
            original.setThisMinChgQusi(result[1]);
            original.setThisMaxChgLv(result[2]);
            original.setThisMaxChgQusi(result[3]);
            original.setThisAveChgLv(result[4]);
            original.setThisAveChgQusi(result[5]);
        }

    }

    private String[] calculateChangeRateAndChangeTrends(Double thisMinVal, Double thisMaxVal, Double thisAveVal,
                                                        Double prevMinVal, Double prevMaxVal, Double prevAveVal) {
        Double deltaMin = thisMinVal / prevMinVal - 1;
        Double deltaMax = thisMaxVal / prevMaxVal - 1;
        Double deltaAvg = thisAveVal / prevAveVal - 1;

        return new String[]{String.format("%.2f", deltaMin),
                calculateChangeTrends(deltaMin),
                String.format("%.2f", deltaMax),
                calculateChangeTrends(deltaMax),
                String.format("%.2f", deltaAvg),
                calculateChangeTrends(deltaAvg)};
    }

    private String calculateChangeTrends(Double delta) {
        return delta > 0 ? "上升" : (delta == 0 ? "不变" : "下降");
    }

    private void fillAlarmAndWarn(SensorReport update, Sensor original) {
        Double latestNewVal = Double.valueOf(update.getReportVal());
        Optional<Double> orginalAlarmLimit = Optional.ofNullable(Double.valueOf(original.getAlarmLimitVal()));
        Optional<Double> originalWarnLimit = Optional.ofNullable(Double.valueOf(original.getWarnLimitVal()));
        int deltaAlarm = orginalAlarmLimit.isPresent() ? (latestNewVal > orginalAlarmLimit.get() ? 1 : 0) : 0;
        int deltaWarn = originalWarnLimit.isPresent() ? (latestNewVal > originalWarnLimit.get() ? 1 : 0) : 0;
        // split aleam and warn, assume the direction is increase, warn threshold is greater than alarm threshold
        // we only store for warn in sensorWarn data.
        if (deltaWarn > 0) {
            int newWarnNum = (original.getWarnNum() != null ? Integer.valueOf(original.getWarnNum()) : 0) + 1;
            original.setWarnNum(newWarnNum);
        } else if (deltaAlarm > 0) {
            int newAleamNum = (original.getAleamNum() != null ? Integer.valueOf(original.getAleamNum()) : 0) + 1;
            original.setAleamNum(newAleamNum);
        }

        if (deltaAlarm != 0 || deltaWarn != 0) {
            int newAlertAllNum = (original.getAlertAllNum() != null ? Integer.valueOf(original.getAlertAllNum()) : 0) + 1;
            original.setAlertAllNum(newAlertAllNum);
        }
    }

    private void fillHistorical(Double latest, Sensor original) {
        // historical min, max, historical max,
        int count = original.getReportNum();
        Double previousMin = original.getMinVal() != null ? Double.valueOf(original.getMinVal()) : null;
        Double previousMax = original.getMaxVal() != null ? Double.valueOf(original.getMaxVal()) : null;
        Double avgVal = original.getAveVal() != null ? Double.valueOf(original.getAveVal()) : null;

        String[] result = calcNewMinMaxAvg(latest,
                previousMin, previousMax, avgVal, BigInteger.valueOf(count));
        original.setMinVal(result[0]);
        original.setMaxVal(result[1]);
        original.setAveVal(Double.valueOf(result[2]));
    }

    // assume values are double
    /**
     * 根据当前时间返回今天的小时段数组
     *
     * @return
     */
    private String[] getTodayHours() {
        Calendar calendar = Calendar.getInstance();
        int hours = calendar.get(Calendar.HOUR_OF_DAY);
        String[] strings = new String[hours + 1];
        for (int i = 0; i <= hours; i++) {
            if (i < 10) {
                strings[i] = "0" + String.valueOf(i);
            } else {
                strings[i] = String.valueOf(i);
            }
        }
        return strings;
    }

    private String[] calcNewMinMaxAvg(Double newVal, Double minVal, Double maxVal, Double avgVal, BigInteger cnt) {
        final Double newMaxVal = maxVal == null ? newVal : (maxVal < newVal ? newVal : maxVal);
        final Double newMinVal = (minVal == null || minVal == 0) ? newVal : ((minVal > newVal) ? newVal : minVal);

        final Double newAvgVal = avgVal == null ? newVal :
                (avgVal * (cnt.intValue() - 1) + newVal) / (cnt.intValue());
        return new String[]{String.format("%.2f", newMinVal),
                String.format("%.2f", newMaxVal),
                String.format("%.2f", newAvgVal)};
    }

    /**
     * 比较时间字符串的大小
     *
     * @param DATE1
     * @param DATE2
     * @return
     */
    public int compare_date(String DATE1, String DATE2) {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            Date dt1 = df.parse(DATE1);
            Date dt2 = df.parse(DATE2);
            if (dt1.getTime() > dt2.getTime()) {
                return 1;
            } else if (dt1.getTime() < dt2.getTime()) {
                return -1;
            } else {
                return 0;
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return 0;
    }

    /**
     * 获取最新的一条数据
     *
     * @param sensorReportId
     * @param sensorId
     * @return
     */
    public JSONObject findLatest(Long sensorReportId, Long sensorId) {
        SensorReport sensorReport = repository.findById(sensorReportId).orElse(null);
        String[] todayArr = new String[1];
        todayArr[0] = sensorReport.getReportVal();
        String[] yestodayArr = new String[0];
        JSONObject json = new JSONObject();
        json.put("today", todayArr);
        json.put("yesterday", yestodayArr);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("sensor_report", json);
        JSONObject jsonObjectEnd = new JSONObject();
        jsonObjectEnd.put("channel", "SENSOR_REPORT@".concat(String.valueOf(sensorId)));
        jsonObjectEnd.put("data", jsonObject);
        return jsonObjectEnd;
    }

    /**
     * 获取昨天和今天每分钟的数据
     *
     * @param sensorId
     * @return
     */
    public JSONObject findAllBySensorId(Long sensorId) throws Exception {
        //获取当前时间
        long currentTimeMillis = System.currentTimeMillis();

        //获取今天日期.
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat secondFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String today = dateFormat.format(new Date());

        //获取昨天日期
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, -1);
        Date d = cal.getTime();
        String yesterday = dateFormat.format(d);

        //获取全部排序后的数据
        List<SensorReport> all = repository.findAllBySeneorId(sensorId);

        //数据处理
        List<String> todayList = new ArrayList<>();
        List<String> yesterdayList = new ArrayList<>();
        String[] hourArr = new String[]{"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"};
        String[] minuteArr = new String[]{"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59"};
        for (int h = 0; h < 24; h++) {
            for (int m = 0; m < 60; m++) {
                //今天截至当前时间每分钟的数据
                String todayStr = today + " " + hourArr[h] + ":" + minuteArr[m] + ":59";
                Date todayDate = secondFormat.parse(todayStr);
                long timelag = todayDate.getTime() - currentTimeMillis;//时差

                if (todayDate.getTime() < currentTimeMillis) {//只查询小于当前时间的数据
                    List<SensorReport> thisTodayMomentList = all.size() > 0 ? all.stream().filter(s -> s.getGenTime().getTime() <= todayDate.getTime()).collect(Collectors.toList()) : new ArrayList<SensorReport>();
                    String todayVal = thisTodayMomentList.size() > 0 ? thisTodayMomentList.get(0).getReportVal() : "0";
                    todayList.add(todayVal);
                } else if (timelag >= 0 && timelag <= 60000) {//当前时间与指定时间在同一分钟，则取小于当前时间的数据
                    String todayVal = all.size() > 0 ? all.stream().filter(s -> s.getGenTime().getTime() <= currentTimeMillis).collect(Collectors.toList()).get(0).getReportVal() : "0";
                    todayList.add(todayVal);
                }

                //昨天每分钟的数据
                String yesterdayStr = yesterday + " " + hourArr[h] + ":" + minuteArr[m] + ":59";
                Date yesterdayDate = secondFormat.parse(yesterdayStr);

                List<SensorReport> thisYesMomentList = all.size() > 0 ? all.stream().filter(s -> s.getGenTime().getTime() <= yesterdayDate.getTime()).collect(Collectors.toList()) : new ArrayList<SensorReport>();
                String yesterdayVal = thisYesMomentList.size() > 0 ? thisYesMomentList.get(0).getReportVal() : "0";
                yesterdayList.add(yesterdayVal);
            }
        }
        String[] newTodayArr = new String[todayList.size()];
        String[] newYesterdayArr = new String[yesterdayList.size()];

        //组装返回数据
        JSONObject json = new JSONObject();
        json.put("today", todayList.toArray(newTodayArr));
        json.put("yesterday", yesterdayList.toArray(newYesterdayArr));
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("sensor_report", json);
        JSONObject jsonObjectEnd = new JSONObject();
        jsonObjectEnd.put("channel", "SENSOR_REPORT@".concat(String.valueOf(sensorId)));
        jsonObjectEnd.put("data", jsonObject);
        return jsonObjectEnd;
    }
}